/**
 * `vscdd.follows` 设置帮助函数
 */

import * as vscode from 'vscode';
import * as biliapi from './api';
import { AuthConfig, isLoginValid } from './auth';
import { Await, distance } from './utils';
import { updateConfiguration } from './vscode-helper';


export interface FollowsItem {
  uid: number,
  /** @description 昵称 */
  uname?: string,
  /** @description 直播间号 */
  room_id?: number,
  /** @description 是否特别关注 */
  special?: boolean,
  /** @description 额外描述信息（供模糊查询使用） */
  hint?: string
}

/**
 * 从 B 站拉取数据填写 `follows` 设置项中省去的部分
 * @borrows config.vscdd.follows
 * @param [check_room_id] - 检查 `room_id` 直播间号是否填写正确，默认不检查
 * @param [reset_special] - 将 `special` 特别关注设置恢复与 B 站设置一致，默认不覆盖
 */
export async function updateFollows({check_room_id=false, reset_special=false}) {
  // 获取验证信息
  let { SESSDATA } = vscode.workspace.getConfiguration('vscdd').get('auth') as AuthConfig;
  if (!isLoginValid()) {
    let choice = await vscode.window.showWarningMessage(
        '未设置有效登录信息，只能拉取前 250 个关注，是否继续？',
        'Ok', 'Cancel');
    if (typeof choice === 'undefined' || choice !== 'Ok')
      return;
    SESSDATA = undefined;
  }
  // 拉取关注列表
  let follows = vscode.workspace.getConfiguration('vscdd').get('follows') as FollowsItem[];
  for (let f of follows) {
    // 确认是否需要进行更新
    let should_update = false;
    should_update ||= check_room_id || reset_special;
    should_update ||= typeof f.uname === 'undefined' || typeof f.room_id === 'undefined';
    if (!should_update)
      continue;
    // 拉取并更新
    const info = await biliapi.get_user_info(f.uid, SESSDATA);
    f.uname = info.name;
    f.room_id = info.roomid;
    if (reset_special)
      f.special = info.special;
  }
  updateConfiguration('vscdd.follows', follows);
}

export async function getFollowListWithProgress(uid: number, SESSDATA: string)
      : ReturnType<typeof biliapi.get_follow_list> {
  return vscode.window.withProgress(
    { title: '正在拉取关注列表', cancellable: false,
      location: vscode.ProgressLocation.Notification },
    async (progress, token) => {
      let follows: Await<ReturnType<typeof biliapi.get_follow_list>> = []
      for (let pn = 0; ; pn++) {
        let fp = await biliapi._get_follow_page(uid, pn, SESSDATA);
        if (fp.list.length === 0)
          break;
        for (let f of fp.list) {
          follows.push({
            uid: f.uid, uname: f.uname, sign: f.sign, special: f.special!
          });
        }
        progress.report({increment: fp.list.length / fp.total * 100.});
      }
      return follows;
    }
  );
}

/**
 * 从 B 站拉取关注列表
 * @borrows config.vscdd.follows
 * @param [opt.only_vup=true] - 仅将虚拟主播合并入 `follows` 设置项，通过其直播间分区
 *    是否为，默认为 true
 * @param [opt.sign_as_hint=true] - 让用户的签名作为 fuzzy-find 辅助描述，默认为 true
 * @param [opt.skip_non_livers=true] - 跳过非主播用户，默认未 true
 * @param [opt.merge_mode='skip'] - 与本地设置的合并行为
 *    * `skip` - 默认，跳过设置中已申明用户
 *    * `truncate` - 删除本地配置，拉取信息重新做人
 *    * `overwrite` - 覆写本地已有设置
 * @param [target] - 目标更新配置文件，若未提供则更新至默认位置 @see updateConfiguration
 * @throws {Error}
 *    * `no valid auth` - 无可用登录信息
 */
export async function mergeFollowsFromSite(
      opt?: {
        only_vup?: boolean,
        sign_as_hint?: boolean,
        skip_non_livers?: boolean,
        merge_mode?: 'skip' | 'truncate' | 'overwrite'
      },
      target?: vscode.ConfigurationTarget) {
  // parameter setup
  if (typeof opt === 'undefined')
    opt = {};
  opt.only_vup = opt.only_vup ?? true;
  opt.sign_as_hint = opt.sign_as_hint ?? true;
  opt.skip_non_livers = opt.skip_non_livers ?? true;
  opt.merge_mode = opt.merge_mode ?? 'skip';
  if (!isLoginValid()) {
    vscode.window.showErrorMessage('无有效登录信息！');
    throw new Error('no valid auth');
  }
  // get local config
  const {uid, SESSDATA} = vscode.workspace.getConfiguration('vscdd').get('auth') as Required<AuthConfig>;
  let local_follows: FollowsItem[];
  if (opt.merge_mode === 'truncate')
    local_follows = [];
  else
    local_follows = vscode.workspace.getConfiguration('vscdd').get('follows')!;
  /** a set of UIDs already in local config, the value is its index in `local_follows` */
  let followed = new Map<number, number>();
  local_follows.forEach(({uid}, i, _) => { followed.set(uid, i); })
  // pull remote config
  let remote_follows = await getFollowListWithProgress(uid, SESSDATA);
  // try merge
  let is_cancelled = !await vscode.window.withProgress(
    { title: '正在将拉取的关注列表与本地配置合并', cancellable: true,
      location: vscode.ProgressLocation.Notification },
    /**
     * @returns has finished normally
     */
    async (progress, token) => {
      let curr_finished = 0;
      for (let rf of remote_follows) {
        curr_finished += 1;
        console.debug(`curr_finished ${curr_finished}, local_follows.length ${local_follows.length}, processing`);
        console.debug(rf);
        if (curr_finished % 10 === 0) {
          // checkpoint, in case failed (presumably due to throttling)
          await updateConfiguration('vscdd.follows', local_follows, target);
          progress.report({increment: 10 / remote_follows.length * 100.});
        }
        if (token.isCancellationRequested)
          return false;
        const roomid = await biliapi.get_room_of_uid(rf.uid);
        // skip on non livers
        if (typeof roomid === 'undefined' && opt!.skip_non_livers)
          continue;
        // skip on non vups
        if (opt!.only_vup && (typeof roomid === 'undefined' || !await biliapi.is_vup_room(roomid)))
          continue;
        // skip on seen
        if (opt!.merge_mode === 'skip' && followed.has(rf.uid))
          continue;
        // get insert / update item slot in `local_follows`
        const is_new_entry = !followed.has(rf.uid);
        let lf: FollowsItem;
        if (is_new_entry) {
          //opt.merge_mode: 'skip' | 'truncate'
          lf = {
            uid: rf.uid, uname: rf.uname, room_id: roomid,
            special: await biliapi.is_special_follow(rf.uid, SESSDATA),
            hint: opt!.sign_as_hint ? rf.sign : undefined
          };
        }
        else {
          //opt.merge_mode: 'overwrite';
          lf = local_follows[followed.get(rf.uid)!];
          if (opt!.sign_as_hint) {
            lf.hint = (lf.hint ?? '').trim();
            if (lf.hint.length === 0)
              lf.hint = rf.sign;
            else if (rf.sign.includes(lf.hint))
              lf.hint = rf.sign
            else if (lf.hint.includes(rf.sign))
              /*pass*/0;
            else if (distance(lf.hint, rf.sign) > 1.)
              lf.hint = `${rf.sign} ${lf.hint}`;
          }
        }
        // write to `local_follows`
        if (is_new_entry) {
          followed.set(lf.uid, local_follows.length);
          local_follows.push(lf);
        }
        else {
          //// automagically writen thanks to direct binding
        }
      }
      // update to config file
      await updateConfiguration('vscdd.follows', local_follows, target);
      return true;
    }
  );
  if (is_cancelled)
    return;
  vscode.window.showInformationMessage('成功合并关注列表！');
}
